package com.segersten.othello.bot;

import java.util.Map;
import java.util.Random;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import com.google.common.collect.Lists;
import com.segersten.othello.model.Color;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.game.Action;
import com.segersten.othello.service.akka.game.BoardActionResponse;
import com.segersten.othello.service.akka.game.BoardState2;
import com.segersten.othello.service.akka.lobby.DomainPlayer;
import com.segersten.othello.service.akka.lobby.Game;
import com.segersten.othello.service.akka.lobby.PlayerScore;
import com.segersten.othello.service.rest.ActionRequest;
import com.segersten.othello.service.rest.LobbyStateResponse;
import com.segersten.othello.service.rest.LoginRequest;
import com.segersten.othello.service.rest.RegisterRequest;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class BotLauncher
{
    static BotLauncher obj;

    public static final void main(String[] args)
    {
        sout(Lists.newArrayList(args).toString());
        if (args.length!=5) {
            sout("Expects host, port, nick, auth, iterations");
            return;
        }
        String host = args[0];
        String port = args[1];
        String nick = args[2];
        String auth = args[3];
        String iterations = args[4];
        sout("Connecting to http://"+host+":"+port+" with "+nick+" usign auth "+auth + ", iterations="+iterations);
        
        for(int i=0;i<Integer.parseInt(iterations);i++) {
            obj = new BotLauncher();
            obj.play(host, Integer.parseInt(port), nick, auth);
        }
    }

    private void play(String url, int port, String nick, String auth)
    {
        sout("Starting bots");
        WebResource service = getWebResource(url, port);

        soutn("Login " + nick+ " - ");
        Auth serviceAuth = new Auth("bot", auth);
        Auth playerAuth = service.path("game").path("lobby").path("login").
            accept(MediaType.APPLICATION_XML).post(Auth.class, new LoginRequest(nick, serviceAuth));
        sout("OK");
        
        Player player = new Player(nick, playerAuth);
        
        soutn("Player score - ");
        PlayerScore score = service.path("game").path("lobby").path("score").path(player.domainPlayer().getDomain()).path(player.getNickname()).accept(MediaType.APPLICATION_XML).get(PlayerScore.class);;
        sout(Integer.toString(score.getScore()));
        
        soutn("Find game - ");
        Game game = findGame(service, player);

        if (game == null) {
            sout("not found");
            soutn("Register on a new game ");
            RegisterRequest req = new RegisterRequest(player);
            service.path("game").path("lobby").path("register").accept(MediaType.TEXT_PLAIN).post(String.class, req);
            sout(" OK");
            game = waitForGame(service, player);
        } else {
            sout("found, continue on that game");
        }
        
        Color myColor = getMyColor(game.getPlayers(), player.domainPlayer());
        sout("Start playing color " + myColor);
        playGame(service, game, myColor, player);
        sout("Done");
        soutn("Change  - ");
        PlayerScore score2 = service.path("game").path("lobby").path("score").path(player.domainPlayer().getDomain()).path(player.getNickname()).accept(MediaType.APPLICATION_XML).get(PlayerScore.class);;
        sout(Integer.toString(score2.getScore()-score.getScore()));
        sout("Player score - " + score2.getScore());
    }

    private Color getMyColor(Map<Color, DomainPlayer> players, DomainPlayer domainPlayer)
    {
        if (players.get(Color.BLACK).equals(domainPlayer)) {
            return Color.BLACK;
        }
        if (players.get(Color.WHITE).equals(domainPlayer)) {
            return Color.WHITE;
        }
        throw new IllegalStateException(String.format("Player %s is not in game %", domainPlayer, players));
    }

    private void playGame(WebResource service, Game game, Color myColor, Player player)
    {
        String boardReference = game.getBoardReference().getReference();
        BoardState2 state = service.path(boardReference).accept(MediaType.APPLICATION_XML).get(BoardState2.class);

        int round = 0;
        while (state.getNextColor() != null) {
            round++;
            if (myColor.equals(state.getNextColor())) {
                soutn("Post action - ");
                ActionRequest action = new ActionRequest(player.getAuth(), new Action(state.getAvailablePoints()
                        .iterator().next(), state.getNextColor()));
                String resp = service.path(boardReference).accept(MediaType.TEXT_PLAIN)
                        .post(String.class, action);
                sout(resp);
                if (false == resp.equals("OK")) {
                    throw new IllegalStateException("Failed to play");
                }
            }
            soutn("Get state - ");
            state = service.path(boardReference).path(Integer.toString(state.getRound()))
                    .accept(MediaType.APPLICATION_XML).get(BoardState2.class);
            sout("OK");
            try {
                Thread.currentThread().sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private Game waitForGame(WebResource service, Player p)
    {
        int count = 0;
        while (true) {
            Game game = findGame(service, p);
            if (game != null) {
                sout("OK");
                return game;
            }
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            count++;
            if (count % 100 == 0) {
                tick();
            }
        }
    }

    private Game findGame(WebResource service, Player player)
    {
        LobbyStateResponse state = service.path("game").path("lobby").path("list").accept(MediaType.APPLICATION_XML)
                .post(LobbyStateResponse.class, player.getAuth());
        if (state.getGames().size() > 0) {
            if (state.getGames().size() > 1) {
                throw new IllegalStateException("Expected one game, got " + state.getGames().size());
            }
            sout("");
            return state.getGames().iterator().next();
        } else {
            return null;
        }

    }

    private WebResource getWebResource(String url, int port)
    {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        return client.resource(UriBuilder.fromUri("http://" + url + ":" + port).build());
    }

    private static void sout(String message)
    {
        System.out.println(message);
    }
    
    private static void soutn(String message)
    {
        System.out.print(message);
    }

    private void tick()
    {
        System.out.print(".");
    }

}
